package com.hk.commons.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;

/**
 * @author Kevin
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class ProxyUtils {

    @SneakyThrows
    public static Object getTargetObject(Object candidate) {
        if (AopUtils.isAopProxy(candidate) && candidate instanceof Advised advised) {
            Object target = advised.getTargetSource().getTarget();
            if (target != null) {
                return getTargetObject(target);
            }
        }
        return candidate;
    }

    /**
     * 获取代理是真实对象
     *
     * @param candidate candidate
     * @param clazz clazz
     */
    @SneakyThrows
    public static <T> T getTargetObject(Object candidate, Class<T> clazz) {
        if (AopUtils.isAopProxy(candidate) && candidate instanceof Advised advised) {
            Object target = advised.getTargetSource().getTarget();
            if (target != null) {
                return getTargetObject(target, clazz);
            }
        }
        return clazz.cast(candidate);
    }

//    private static Object getCglibProxy(Object proxy) throws Exception {
//        var h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
//        ReflectionUtils.makeAccessible(h);
//        var dynamicAdvisedInterceptor = h.get(proxy);
//        var advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
//        ReflectionUtils.makeAccessible(advised);
//        return ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
//    }
//
//    private static Object getJdkDynamicProxy(Object proxy) throws Exception {
//        var h = proxy.getClass().getSuperclass().getDeclaredField("h");
//        ReflectionUtils.makeAccessible(h);
//        var aopProxy = (AopProxy) h.get(proxy);
//        var advised = aopProxy.getClass().getDeclaredField("advised");
//        ReflectionUtils.makeAccessible(advised);
//        return ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
//    }
}
